﻿//-----------------------------------------------------------------------
// <copyright>
//     Copyright (c) Artur Mustafin. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.IO;
using Excel.Expressions.Compiler;
using Expressions.Compiler;
using Expressions.Compiler.Interfaces;
using Excel.Expressions.Interfaces;

namespace Excel.Expressions.Classes
{
    public class Instructions
    {
        private readonly Dictionary<int, Instruction> _instructions;

        public Instructions()
        {
            _instructions = new Dictionary<int, Instruction>();
        }

        public void Update(Cell cell, string text, CellExpression expression, Action<Cell> action)
        {
            Remove(cell);
            cell.Unsubscribe();
            cell.Visit(action);
            cell.Expression = expression;
            cell.Text = text;
            cell.Subscribe();
            cell.Visit(action);
            Add(cell);
        }

        public void Add(Cell cell)
        {
            if (!_instructions.ContainsKey(cell.Id))
            {
                _instructions.Add(cell.Id, new Instruction() { Cell = cell, Expression = cell.Expression });
            }
        }

        public void Remove(Cell cell)
        {
            if (_instructions.ContainsKey(cell.Id))
            {
                _instructions.Remove(cell.Id);
            }
        }

        public void Load(string path, Action<Cell> action)
        {
            _instructions.Clear();

            Nonterminal grammar = Nonterminals.cell_assigment_expression;
            Expression<CellExpression> expression = Expressions.cell_assigment_expression;

            grammar.Initialize();
            grammar.Validate();

            foreach (string line in File.ReadLines(path))
            {
                if (line == null || string.IsNullOrEmpty(line) || string.IsNullOrWhiteSpace(line))
                {
                    throw new NotSupportedException("Initialization of the blank input line is not supported");
                }

                Parser input = line;
                if (!grammar.Parse(input))
                {
#if TRACE
                    Parser.ClearTraceLog();
#endif
                    throw new NotSupportedException("Initialization of the invalid assigment expression is not supported");
                }
#if TRACE
                Parser.WriteTraceLog();
#endif
                Assignment instruction = expression[input.Tree] as Assignment;
                if (instruction == null)
                {
                    throw new NotSupportedException("Assigment of the null instruction is not supported");
                }

                CellReference reference = instruction.CellReference;
                if (reference == null)
                {
                    throw new NotSupportedException("Assigment of the null instruction reference is not supported");
                }

                CellExpression instructionExpression = instruction.Expression;
                if (instructionExpression == null)
                {
                    throw new NotSupportedException("Assigment of the null instruction expression is not supported");
                }
                Cell current = reference.Cell;

                Remove(current);
                current.Text = instruction.Text;
                current.Expression = instruction.Expression;
                Add(current);
            }

            List<Instruction> instuctions = new List<Instruction>(_instructions.Values);

            foreach (Instruction instruction in instuctions)
            {
                Cell cell = instruction.Cell;
                CellExpression eval = instruction.Expression;
                if (!cell.Validate(eval))
                {
                    throw new NotSupportedException("Initialization of the circle referenced instruction is not supported");
                }
                cell.Subscribe();
            }
            foreach (Instruction instruction in instuctions)
            {
                Cell cell = instruction.Cell;
                cell.Visit(action);
            }
        }

        public void Save(string path)
        {
            List<string> lines = new List<string>();
            if (_instructions.Count > 0)
            {
                foreach (var instruction in _instructions.Values)
                {
                    lines.Add(string.Format("{0}={1}", instruction.Cell.Name, instruction.Cell.Text));
                }
            }
            File.WriteAllLines(path, lines);
        }

        public IEnumerable<Cell> Cells
        {
            get
            {
                List<Cell> cells = new List<Cell>();
                foreach (Instruction instruction in _instructions.Values)
                {
                    cells.Add(instruction.Cell);
                }
                return cells;
            }
        }

        public void Clear()
        {
            if (_instructions.Count > 0)
            {
                foreach (Instruction instruction in _instructions.Values)
                {
                    instruction.Cell.Clear();
                }
                _instructions.Clear();
            }
        }
    }
}
